package com.bite.job.handler;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bite.common.core.contants.CacheConstants;
import com.bite.common.core.contants.Constants;
import com.bite.common.core.enums.ExamListType;
import com.bite.job.domain.exam.Exam;
import com.bite.job.domain.message.Message;
import com.bite.job.domain.message.MessageText;
import com.bite.job.domain.message.vo.MessageTextVO;
import com.bite.job.domain.user.UserScore;
import com.bite.job.mapper.exam.ExamMapper;
import com.bite.job.mapper.message.MessageMapper;
import com.bite.job.mapper.message.MessageTextMapper;
import com.bite.job.mapper.user.UserExamMapper;
import com.bite.job.mapper.user.UserSubmitMapper;
import com.bite.job.service.message.MessageService;
import com.bite.job.service.messageText.MessageTextService;
import com.bite.redis.config.RedisService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Component
@Slf4j
public class ExamXxlJob {

    @Autowired
    private RedisService redisService;

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private UserSubmitMapper userSubmitMapper;

    @Autowired
    private MessageMapper messageMapper;

    @Autowired
    private MessageTextMapper messageTextMapper;

    @Autowired
    private MessageTextService messageTextService;

    @Autowired
    private MessageService messageService;

    @Autowired
    private UserExamMapper userExamMapper;


    @XxlJob("examListOrganizeHandler")
    public void examListOrganizeHandler() {

        //定时任务逻辑-统计哪些竞赛应该存入未完赛的列表,哪些是历史竞赛

        log.info("定时任务被调用");
        List<Exam> unFinishList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                .gt(Exam::getEndTime, LocalDateTime.now())
                .eq(Exam::getStatus, Constants.TRUE)
                .orderByDesc(Exam::getCreateTime));

        refreshCache(unFinishList,CacheConstants.EXAM_UNFINISHED_LIST);

        List<Exam> historyList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                .le(Exam::getEndTime, LocalDateTime.now())
                .eq(Exam::getStatus, Constants.TRUE)
                .orderByDesc(Exam::getCreateTime));

        refreshCache(historyList,CacheConstants.EXAM_HISTORY_LIST);

    }


    //竞赛结果统计
    @XxlJob("examResultHandler")
    public void examResultHandler(){
        //统计排名
        //对前一天的竞赛进行统计
        LocalDateTime now = LocalDateTime.now();  //当前时间
        LocalDateTime minusDateTime = now.minusDays(1);  //前一天时间
        //拿到符合范围的所有竞赛id
        List<Exam> examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId,Exam::getTitle)
                .ge(Exam::getEndTime, minusDateTime)
                .le(Exam::getEndTime, now)
                .eq(Exam::getStatus, Constants.TRUE));
        if (CollectionUtil.isEmpty(examList)){
            return;
        }
        //竞赛中用户排名统计  拿到竞赛id
        Set<Long> examIdSet = examList.stream().map(Exam::getExamId).collect(Collectors.toSet());
        //计算所有竞赛所对应的总分,排名情况
        List<UserScore> userScoreList = userSubmitMapper.selectUserScoreList(examIdSet); //多个竞赛
        //根据竞赛分类  每个竞赛的排名数据
        Map<Long, List<UserScore>> userScoreMap = userScoreList.stream().collect(Collectors.groupingBy(UserScore::getExamId));
        //生成消息
        createMessage(examList,userScoreMap);

    }


    //生成message
    private void createMessage(List<Exam> examList,Map<Long, List<UserScore>> userScoreMap){
        List<MessageText> messageTextList = new ArrayList<>();
        List<Message> messageList = new ArrayList<>();
        //为每个竞赛生成消息
        for (Exam exam : examList){
            Long examId = exam.getExamId();
            List<UserScore> userScoreList = userScoreMap.get(examId);
            //参与竞赛的用户数量
            int totalUser = userScoreList.size();
            int examRank = 1;

            for (UserScore userScore : userScoreList){
                String msgTitle = exam.getTitle() + "--排名情况";
                String msgContent = "您所参与的竞赛: "+ exam.getTitle()
                        + ",本次参与竞赛一共" + totalUser + "人,您排名第" +examRank + "名!";
                //排名
                userScore.setExamRank(examRank);

                //存储消息内容
                MessageText messageText = new MessageText();
                messageText.setMessageTitle(msgTitle);
                messageText.setMessageContent(msgContent);
                messageText.setCreateBy(Constants.SYSTEM_USER_ID);
                messageTextList.add(messageText);
                //messageTextMapper.insert(messageText);  //循环插入,多次调用数据库,消耗越多

                //存储发送消息内容
                Message message = new Message();
                message.setSendId(Constants.SYSTEM_USER_ID);
                message.setCreateBy(Constants.SYSTEM_USER_ID);
                message.setRecId(userScore.getUserId());
                messageList.add(message);

                examRank++;
            }
            //存储排名相关
            userExamMapper.updateUserScoreAndRank(userScoreList);
            redisService.rightPushAll(getExamRankListKey(examId),userScoreList);
        }
        //批量插入
        messageTextService.batchInsert(messageTextList);
        Map<String,MessageTextVO> messageTextVOMap = new HashMap<>();
        for (int i = 0; i < messageTextList.size(); i++) {
            MessageText messageText = messageTextList.get(i);
            //存入消息列表的value
            MessageTextVO messageTextVO = new MessageTextVO();
            BeanUtil.copyProperties(messageText,messageTextVO);
            String msgDetailKey = getMsgDetailKey(messageText.getTextId());
            //批量插入
            messageTextVOMap.put(msgDetailKey,messageTextVO);

            Message message = messageList.get(i);
            message.setTextId(messageText.getTextId());
        }
        messageService.batchInsert(messageList);

        //存入redis--将竞赛排名信息根据用户id分组
        //用户消息列表
        Map<Long, List<Message>> userMsgMap = messageList.stream().collect(Collectors.groupingBy(Message::getRecId));
        Iterator<Map.Entry<Long, List<Message>>> iterator = userMsgMap.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<Long, List<Message>> entry = iterator.next();
            Long recId = entry.getKey();
            String userMsgListKey = getUserMsgListKey(recId);
            List<Long> userMsgTextIdList = entry.getValue().stream().map(Message::getTextId).toList();
            redisService.rightPushAll(userMsgListKey,userMsgTextIdList);
        }
        //消息详情信息
        redisService.multiSet(messageTextVOMap);

    }

    public void refreshCache(List<Exam> examList,String examListKey) {
        //又可能数据库也没数据 直接返回,不进行操作
        if (CollectionUtil.isEmpty(examList)) {
            return;
        }

        //批量存储信息
        Map<String, Exam> examMap = new HashMap<>();
        //列表缓存也要存
        List<Long> examIdList = new ArrayList<>();
        for (Exam exam : examList) {
            examMap.put(getDetailKey(exam.getExamId()), exam);
            examIdList.add(exam.getExamId());
        }
        redisService.multiSet(examMap);  //刷新详情缓存
        if (redisService.hasKey(examListKey)){
            redisService.deleteObject(examListKey);  //先把list的key清除,避免之前的数据重复
        }
        redisService.rightPushAll(examListKey, examIdList);      //尾插法,刷新列表缓存
    }



    private String getExamListKey(Integer examListType) {
        //判断是历史竞赛还是未完成竞赛,返回key
        if (ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)) {
            return CacheConstants.EXAM_UNFINISHED_LIST;
        } else if (ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)) {
            return CacheConstants.EXAM_HISTORY_LIST;
        }
        return "";
    }

    private String getDetailKey(Long examId) {
        return CacheConstants.EXAM_DETAIL + examId;
    }

    //用户消息列表key
    private String getUserMsgListKey(Long userId){
        return CacheConstants.USER_MESSAGE_LIST + userId;
    }

    //消息详细信息key
    private String getMsgDetailKey(Long textId){
        return CacheConstants.MESSAGE_DETAIL + textId;
    }

    private String getExamRankListKey(Long examId){
        return CacheConstants.EXAM_RANK_LIST+examId;

    }

}




